Medium 清新閱讀版
:連結
今天是鐵人賽第7天了!
前面幾天我們介紹了測試3A原則,也介紹了許多 Assertion 函數,今天就讓我們實際演練吧!
過去的經驗中,最常用自動化測試來測式的對象,大概就是API了,而前後端分離也是目前 Web 開發界常用的模式,因此我們就以 API 測試來演練吧!
HTTP Status Code 的驗證,應該是最常遇到的 API 測試情境了,其實前面的 Assertion 函數範例中也有展示部分使用方式了,這次讓我們更完整地演練吧!
提醒一點,由於以下的測試會牽涉到資料庫,因此大家要再檢查一下 phpunit.xml
中的資料庫連線設定是否已填上,詳情可參考前Day 6文章內容。
另值得一提的是,為了方便取用路由路徑,在設置路由時,通常都會將路由命名。
<?php
use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
Route::get('/users', function (Request $request) {
$users = User::all();
return response()->json([
'users' => $users,
]);
})->name('get.user.list');
<?php
namespace Tests\Feature;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;
class HttpStatusCodeTest extends TestCase
{
use RefreshDatabase;
public function testCanGetUserListWhenEmpty()
{
$response = $this->get(route('get.user.list'));
$response->assertOk();
// 也可以寫成以下串聯寫法
$this->get(route('get.user.list'))
->assertOk();
}
}
在以上程式碼中, users
資料表尚無任何User資料,但取得User清單的端點也應該要能正常回應,透過以上測試程式碼,可以驗證此行為是否正常運作。
routes/api.php
<?php
use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
Route::get('/users/{id}', function (Request $request, $id) {
$user = User::find($id);
if (empty($user)) {
return response()->json([
'error' => 'Not Found',
], 404);
}
return response()->json([
'user' => $user,
]);
})->where('id', '[0-9]?')->name('get.user');
tests/Feature/HttpStatusCodeTest.php
<?php
namespace Tests\Feature;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;
class HttpStatusCodeTest extends TestCase
{
use RefreshDatabase;
public function testCanGetNotFoundWhenNoGivenUser()
{
$response = $this->get(route('get.user', ['id' => 1,]));
$response->assertNotFound();
// 也可以寫成以下串聯寫法
$this->get(route('get.user', ['id' => 1,]))
->assertNotFound();
}
}
在以上程式碼中, users
資料表尚無任何User資料,因此當嘗試取得 id=1 之 User 資源時,應會找不到,也就是404,透過以上測試程式碼,可以驗證此行為是否正常運作。
routes/api.php
<?php
use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
Route::post('/users', function (Request $request) {
$request->validate([
'name' => 'required|string',
'email' => 'required|email',
'password' => 'required|string',
]);
$user = User::create($request->all());
return response()->json([
'user' => $user,
]);
})->name('store.user');
tests/Feature/HttpStatusCodeTest.php
<?php
namespace Tests\Feature;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;
class HttpStatusCodeTest extends TestCase
{
use RefreshDatabase;
public function testCanResponseUnprocessableWhenInvalidRequest()
{
$response = $this->post(
route('store.user'),
['name' => ''],
['Accept' => 'application/json']
);
$response->assertUnprocessable();
// 也可以寫成以下串聯寫法
$this->post(
route('store.user'),
['name' => ''],
['Accept' => 'application/json']
)
->assertUnprocessable();
}
}
在以上程式碼中, 我們建立了一個可以建立 User 資料的路由端點,與實作建立資料流程,並在這當中建立了驗證請求資料的邏輯,而在下方的測試案例中,我們測試了「當請求內容有缺失時,應回應 422 Unprocessable Entity」這個行為是否正常運作。
除了前面介紹的 HTTP Status Code 驗證,在測試 API 時,另一個常常需要做驗證的部分,應該就是回應JSON的內容本身了,以下就讓我們來看看實例吧!
routes/api.php
<?php
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
Route::get('/users', function (Request $request) {
// 這邊先用假的 User 資料
$users = [
[
'id' => 1,
'name' => 'name1',
'email' => 'user1@email.com',
],
[
'id' => 1,
'name' => 'name1',
'email' => 'user1@email.com',
],
];
return response()->json([
'users' => $users,
]);
})->name('get.user.list');
tests/Feature/HttpStatusCodeTest.php
<?php
namespace Tests\Feature;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;
class HttpStatusCodeTest extends TestCase
{
use RefreshDatabase;
public function testCanGetUserJson()
{
$response = $this->get(route('get.user.list'));
$response->assertJson([
'users' => [
[
'id' => 1,
'name' => 'name1',
'email' => 'user1@email.com',
],
[
'id' => 1,
'name' => 'name1',
'email' => 'user1@email.com',
],
]
]);
// 也可以寫成以下串聯寫法
$this->get(route('get.user.list'))
->assertJson([
'users' => [
[
'id' => 1,
'name' => 'name1',
'email' => 'user1@email.com',
],
[
'id' => 1,
'name' => 'name1',
'email' => 'user1@email.com',
],
]
]);
}
}
在以上程式碼中, 我們建立了一個可以取得 User 清單的端點,並且驗證在呼叫此 API 後,其回應的JSON是否符合預期。
routes/api.php
<?php
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
Route::get('/users', function (Request $request) {
// 這邊先用假的 User 資料
$users = [
[
'id' => 1,
'name' => 'name1',
'email' => 'user1@email.com',
],
[
'id' => 1,
'name' => 'name1',
'email' => 'user1@email.com',
],
];
return response()->json([
'users' => $users,
]);
})->name('get.user.list');
tests/Feature/HttpStatusCodeTest.php
<?php
namespace Tests\Feature;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;
class HttpStatusCodeTest extends TestCase
{
use RefreshDatabase;
public function testCanGetUserJsonStructure()
{
$response = $this->get(route('get.user.list'));
// '*' 代表是每陣列元素
// 此寫法相當於驗證 'users' 是一個陣列
// 每個陣列元素是一個物件
// 每個物件有 id, name, email 這幾個欄位
$response->assertJsonStructure([
'users' => [
'*' => [
'id',
'name',
'email'
],
],
]);
// 也可以寫成以下串聯寫法
$this->get(route('get.user.list'))
->assertJsonStructure([
'users' => [
'*' => [
'id',
'name',
'email'
],
],
]);
}
}
另一種常見的情況,是只驗證 JSON 結構,而略過驗證 JSON 的鍵與值。
以上就是今天的介紹,大家可以多加練習看看!
明天讓我們來練習資料庫測試吧!